void function($) {
// x轴向左，y轴向下
var dtp = 2 * Math.PI / 360;
function sin(degree) {
	return Math.sin(degree * dtp);
}

function cos(degree) {
	return Math.cos(degree * dtp);
}

// 笛卡尔坐标系中向量的旋转矩阵：
// 逆时针：
// | cosA sinA|
// |-sinA cosA|
// 顺时针：
// |cosA -sinA|
// |sinA  cosA|
function rotate(point, center, degree) {
	// 'point' rotate 'degree' base on 'center' in clockwise
	var p1 = [point[0] - center[0], -(point[1] - center[1])],
		p2 = [
			p1[0] * cos(degree) + p1[1] * sin(degree),
			-p1[0] * sin(degree) + p1[1] * cos(degree)
		];
	return [
		center[0] + p2[0],
		center[1] - p2[1]
	]

}

function scale(point, center, time) {
	return [center[0] + (point[0] - center[0]) * time, center[1] + (point[1] - center[1]) * time];
}


function degreeTowardsHorzontal(center, point) {
	// clockwise, [0, 360)
	if (point[0] == center[0]) {
		return point[1] > center[1] ? 90 : 270;
	}
	var p = [point[0] - center[0], -(point[1] - center[1])];
	var degree = Math.atan(p[1] / p[0]) / dtp;
	if (
		(p[0] <= 0 && p[1] <= 0)
	 || (p[0] <= 0 && p[1] >= 0)
	) {
		degree += 180;
	}
	degree = (-degree + 360) % 360;
	return degree;
}

window.transformer = function(offset, originalOffset, size) {

	this.lt = [offset.left - originalOffset.left, offset.top - originalOffset.top];
	this.rt = [this.lt[0] + size.width, this.lt[1]];
	this.originalOffset = originalOffset;
	this.size = size;
	this.transform = {
		degree: 0,
		scale: 1,
		lastOperation: null
	}
}

var fn = transformer.prototype;

fn.rotate = function(center, degree) {
	var self = this;
	var elt = rotate(self.lt, center, degree),
		ert = rotate(self.rt, center, degree);
	self.transform.degree = degreeTowardsHorzontal(elt, ert);
	self.lt = elt;
	self.rt = ert;
	return self;
}

fn.rotateRelative = function(center, degree) {
	var self = this, rc = this.absolutePoint(center);
	return self.rotate(rc, degree);
}

fn.scale = function(center, time) {
	var self = this;
	self.transform.scale *= time;
	self.lt = scale(self.lt, center, time);
	self.rt = scale(self.rt, center, time);
	return self;
}

fn.move = function(dx, dy) {
	var self = this;
	dx = dx || 0;
	dy = dy || 0;
	self.lt[0] += dx;
	self.lt[1] += dy;
	self.rt[0] += dx;
	self.rt[1] += dy;
	return self;
}

fn.moveRelative = function(dx, dy) {
	var self = this,
		dt = self.absolutePoint([dx, dy]);
	return self.move(dt[0] - self.lt[0], dt[1] - self.lt[1]);

}

void function() {
	['move', 'moveRelative', 'scale', 'rotateRelative', 'rotate'].forEach(function(name) {
		var original = fn[name],
			func = function() {
				var self = this;
				var rs, lastOperation,
					args = Array.prototype.slice.call(arguments),
					cur_state = self.clone(),
					has_origin = $.isArray(args[0]);

				rs = original.apply(self, arguments);

				lastOperation = {
					transformer: cur_state,
					has_origin: has_origin,
					abs_origin: has_origin ? args[0] : [0, 0],
					origin: has_origin ? self.percentagePoint(args[0]).toStrArr().join(' ') : [0, 0],
					operation: name,
					args: has_origin ? args.slice(1) : args.slice(0),
					animation: has_origin ?
					  /*rotate scale operation*/
					  args.slice(1)
					:
					/*move operation*/ 
					(function() {
						var t = self.percentagePoint(cur_state.lt);
						t[0] *= -1; t[1] *= -1;
						return t.toStrArr();
					})()
				}

				self.transform.lastOperation = lastOperation;

				return rs === self ? self : rs;
			};
		func.name = name;
		fn[name] = func;
	})	
}()

fn.getStyle = function() {
	return {
		transform: [
	        'translate3d(', this.lt[0], 'px,', this.lt[1], 'px,0px) ',
		    'rotate(', this.transform.degree, 'deg)', ' ',
		    'scale(', this.transform.scale, ')',
	    ].join(''),
	    transformOrigin: '0 0'
	}
}

fn.absolutePoint = function(point) {
	// 相对于原点的坐标
	return rotate([point[0] + this.lt[0], point[1] + this.lt[1]], this.lt, this.transform.degree);
}

fn.relativePoint = function(point) {
	// 相对于左上角的坐标
	var t = rotate(point, this.lt, -this.transform.degree);
	return [t[0] - this.lt[0], t[1] - this.lt[1]];
}
void function() {
var map = {
	'rotate': ['rotate(', 'deg)'],
	'scale': ['scale(', ')'],
	'move': ['translate(', ')'],
}
map.rotateRelative = map.rotate;
map.moveRelative = map.move;

fn.animator = function($div, $container, option) {
	var self = this, rop = $.extend({}, {
		duration: 300,
		timingFunction: 'ease',
		delay: 0,
	}, option), lastOperation = self.transform.lastOperation;

	var $show = $("<div class='transformer-animator'></div>").css({
		position: 'absolute'
	}), transition = 'transform ' +
	        [(rop.duration / 1000) + 's', rop.timingFunction, (rop.delay / 1000) + 's'].join(' ');

	var 
		exec = $.Deferred(), pr = exec.promise(), $cl,
		tm = map[lastOperation.operation],
		animation_property = {
			'transform': 'translateZ(0px) ' + tm[0] + lastOperation.animation.join(',') + tm[1]
		};

	$show
		.empty()
		.width($div.outerWidth())
		.height($div.outerHeight())
		.append(
			$cl = $div.clone().css({
				'transformOrigin': lastOperation.origin,
				'transition': transition,
				'transform': ''
			})
		)
		.css(lastOperation.transformer.getStyle());

	pr.done(function() {
		$show.remove();
		$div.show().css(self.getStyle());
	})
	pr.start = function() {
		$container.append($show);
		$div.hide();
		
		rop.duration < 0 ?
			exec.resolve() :
			requestAnimationFrame(function() {
				requestAnimationFrame(function() {
					$cl.css(animation_property);
					setTimeout(exec.resolve.bind(exec), rop.duration);
				})
			});
		return pr;
	}
	return pr;
}

}()

fn.viewSize = function() {
	var self = this;
	return {
		width: self.size.width * self.transform.scale,
		height: self.size.height * self.transform.scale
	}
}

fn.percentagePoint = function(absolute_point) {
	var self = this,
	    rp = self.relativePoint(absolute_point),
	    size = self.viewSize();
	var t = [rp[0] / size.width, rp[1] / size.height];
	t.toStrArr = function() {
		return [t[0] * 100 + '%', t[1] * 100 + '%'];
	}
	return t;
}

fn.clone = function() {
	var t = {};
	$.extend(true, t, this);
	t.__proto__ = fn;
	return t;
}

}(jQuery)